﻿/*
* Sunbox Comments
* Webpart for adding comments fuctionality
*
* Examples and documentation at: http://sunbox.codeplex.com
* 
* Copyright (c) 2011 - 2012 SKB KONTUR
* Many people have submitted bugs, code, and offered their advice freely. Their support is greatly appreciated.
* 
* Version: 1.1.9 (13/02/2013)
* Requires: jQuery v1.3+
*
* LGPL license:
*   http://www.gnu.org/licenses/lgpl.html
*/

$.fn.extend({
    commentslist: function (options) {
        var aObj = [];
        $(this).each(function () {
            if (!$(this).hasClass("cmts-done")) {
                aObj.push(new CommentsList(this, options));
                $(this).addClass("cmts-done");
            }
        });
        if (aObj.length == 1)
            return aObj[0];
        return aObj;
    }
});



CommentsList = (function () {


    function commentsList(container, options) {
        this.options = $.extend({}, this.defaults(), options);
        this.jqContainer = $(container);
        var $this = this;
        $(".show-comment-form").unbind("click").live("click", function () {
            $this.appendCommentFormClicked.call($this, this);
        });
        $(".comment-submit").unbind("click").live("click", function () {
            $this.submitCommentClicked.call($this, this);
        });
        $(".delete-comment").unbind("click").live("click", function () {
            $this.deleteCommentClicked.call($this, this);
        });

        this.init();

    }

    commentsList.prototype.init = function () {
        //init lang
        if (typeof commentsList.lang[this.options.lang] != 'undefined')
            this.locale = commentsList.lang[this.options.lang];
        else
            this.locale = commentsList.lang.en;

        this.leaveCommentWrapper = $("<div />").addClass("comment-leave");
        this.leaveCommentWrapperBottom = $("<div />").addClass("comment-leave");
        this.commentsLabel = $("<div />").addClass("comment-label");

        this.commentItemsWrapper = $("<div />").addClass("comment-items");

        this.jqContainer.prepend(this.commentsLabel); // comment label (2nd)
        this.jqContainer.prepend(this.leaveCommentWrapper); //leave comment label (1st)

        this.jqContainer.append(this.commentItemsWrapper); // comment items
        this.jqContainer.append(this.leaveCommentWrapperBottom); //leave comment form at bottom

        this.loadCommentItems(this.commentItemsWrapper);

    };
    commentsList.prototype.initLeaveRootCommentForm = function () {
        var leaveCommentLabel = $("<span />").addClass("comment-ajax-link").addClass("show-comment-form").attr("data-parentid", "0")
            .append($("<span />").addClass("text").text(this.locale.leaveCommentText));

        this.commentsLabel.html("<h1>" + this.locale.commentsHeaderText + "<ins class='comments-counter'><i>" + this.commentsCount + "</i></ins></h1>");
        this.leaveCommentWrapper.html("").append(leaveCommentLabel);
        this.leaveCommentWrapperBottom.html("");
        if (this.commentsCount >= this.options.minCommentsToShowBottomForm) {
            this.leaveCommentWrapperBottom.append(leaveCommentLabel.clone());
        }
    };
    commentsList.prototype.loadCommentItems = function (container) {
        //quering
        var currentId = this.getCurrentArticleId();
        var $this = this;
        this.getCommentItems(currentId, function (listItems) {
            if (listItems == null) return;
            $this.commentItems = listItems;
            $this.commentsCount = listItems.length;

            //get root comments first
            var rootItems = $this.getCommentByParentId(0);
            container.html($this.buildCommentItems(rootItems, true));
            //init head
            $this.initLeaveRootCommentForm();
            window.ExecuteOrDelayUntilScriptLoaded(function () {
                if (document.location.hash.length > 0 && document.location.hash.startsWith("#comment")) {
                    var objWorkSpace = document.getElementById('s4-workspace');
                    var position = $(document.location.hash).offset();
                    if (objWorkSpace != null) {
                        document.getElementById('s4-workspace').scrollTop = position.top;
                    }
                    else {
                        window.scrollTo(0, position.top);
                    }
                }
            }, "sp.js");

        });
    };
    //Get child comment items
    commentsList.prototype.getCommentByParentId = function (parentId) {
        var childs = [];
        for (var i = 0; i < this.commentItems.length; i++) {
            if ((parentId == 0 && typeof (this.commentItems[i][this.options.commentsParentFieldName]) == 'undefined') || parseInt(this.commentItems[i][this.options.commentsParentFieldName]) == parseInt(parentId)) {
                childs.push(this.commentItems[i]);
            }
        }
        return childs;
    };
    commentsList.prototype.buildCommentItems = function (items, isroot) {
        if (isroot && items.length == 0) {
            return "<p>" + this.locale.noCommentsText + "</p>";
        }
        var html = "";
        for (var i = 0; i < items.length; i++) {
            var userLookupValue = new SPFieldLookupCollection(items[i][this.options.commentsUserFieldName]);
            var userName = userLookupValue.length > 0 ? userLookupValue[0].LookupValue : "not found";
            var userId = userLookupValue.length > 0 ? userLookupValue[0].LookupId : "0";

            var itemHtml = this.buildCommentItem(items[i].ID, userName, items[i][this.options.commentsTextFieldName], items[i].Created, userId);

            html += this.onRenderItem(itemHtml, items[i]);


            var childItems = this.getCommentByParentId(items[i].ID);
            if (childItems.length > 0) {
                html += "<div class='comment-childs'>";
                html += this.buildCommentItems(childItems, false);
                html += "</div>";
            }
        }
        return html;
    };
    // delete comment click
    commentsList.prototype.deleteCommentClicked = function (elem) {
        if (!confirm(this.locale.deleteComfirmText))
            return;
        var commentId = $(elem).attr("data-parentid");
        this.deleteComment(commentId, true);
    };
    // submit comment
    commentsList.prototype.submitCommentClicked = function (elem) {
        var commentForm = $(elem).closest(".comment-form");
        if (commentForm.length == 0) return;
        //add new comment
        var parentId = commentForm.find(".comment-parentid").val();
        var commentTextArea = commentForm.find(".comment-textarea");
        if (commentTextArea.length == 0) return;
        var commentHtml = CKEDITOR.instances[commentTextArea.attr("id")].getData();
        if (commentHtml.length < 3)
            return;

        var overlay = $("<div />").addClass("comment-overlay");
        commentForm.append(overlay);
        commentHtml = $('<div/>').text(commentHtml).html(); //encode

        var newComment = {};
        newComment[this.options.commentsParentFieldName] = parentId;
        newComment[this.options.commentsArticleIdFieldName] = this.getCurrentArticleId();
        newComment[this.options.commentsTextFieldName] = commentHtml;
        var $this = this;
        SPData.addSPListItems(this.options.commentsListName, newComment, function () {
            if ($this.options.articleListName.length > 0) {
                $this.incrementArticleCommentsCount();
            }
            $this.loadCommentItems($this.commentItemsWrapper);
        });
    };
    commentsList.prototype.incrementArticleCommentsCount = function () {
        var newItem = { ID: this.getCurrentArticleId() };
        newItem[this.options.articleCommentsCountFieldName] = this.commentsCount + 1;
        SPData.updateSPListItems(this.options.articleListName, newItem, function () {
            //Comment added
            alert('news updated');
        });

    };
    // append comment from to layout
    commentsList.prototype.appendCommentFormClicked = function (elem) {
        if ($(elem).hasClass("pressed")) { // close form
            this.closeCommentForm($(elem));
            $(elem).removeClass("pressed");
        } else { // open form
            $(elem).addClass("pressed");
            this.appendCommentForm($(elem));
        }
    };

    // append new comment form
    commentsList.prototype.closeCommentForm = function (obj) {
        var parent = obj.parent();
        var formOuter = parent.find(".comment-form-outer");
        if (formOuter.length > 0) {
            formOuter.hide();
        }
    };
    
    // append new comment form
    commentsList.prototype.onRenderItem = function (html, listitem) {
        return html;
    };

    // append new comment form
    commentsList.prototype.appendCommentForm = function (obj) {
        var parent = obj.parent();
        var formOuter = parent.find(".comment-form-outer");
        if (formOuter.length == 0) {
            formOuter = $("<div />").addClass("comment-form-outer");
            parent.append(formOuter);
        } else {
            formOuter.show();
            return;
        }
        var parentId = obj.attr("data-parentid");
        this.insertCommentForm(parentId, formOuter);
    };

    // insert new comment form to specified container
    commentsList.prototype.insertCommentForm = function (parentId, container) {
        var newCommentFormHtml = this.buildCommentForm(parentId);
        container.html(newCommentFormHtml);
        var textAreaId = $(".comment-textarea", container).attr("id");
        CKEDITOR.replace(textAreaId, { language: this.options.lang });
    };
    //comment form template
    commentsList.prototype.buildCommentForm = function (parentId) {
        var textAreaId = "comment-text-" + this.newGuid();
        var html = "<div class='comment-form'>";
        html += "<input type='hidden' class='comment-parentid' value='" + parentId + "' />";
        html += "<textarea class='comment-textarea' id='" + textAreaId + "'></textarea>";
        html += "<div class='comment-form-buttons'><a class='comment-submit'>" + this.locale.leaveCommentText + "</a></div>";
        html += "</div>";

        return html;
    };
    //comment item template
    commentsList.prototype.buildCommentItem = function (id, user, text, date, userId) {
        if (typeof (text) == 'undefined')
            text = "";
        var jDate = new SPDateTime(date);

        var html = "<div class='comment-item' id='comment" + id + "'>";
        html += "<div class='comment-username" + (this.locale.isWoman(user) ? " woman" : "") + "'><a href='/_layouts/userdisp.aspx?ID=" + userId + "' title='" + this.locale.userInfoLinkTitle + "'>" + user + "</a> <span class='comment-date'>" + this.locale.localizeDateTime(jDate) + "</span> <a href='#comment" + id + "' title='" + this.locale.commentLinkTitle + "'>#</a></div>";
        html += "<div class='comment-text'>" + text + "</div>";
        html += "<div class='comment-response'><span class='comment-ajax-link show-comment-form' data-parentid='" + id + "'><span class='text'>" + this.locale.replyText + "</span></span>";

        if (this.options.enableDelete) {
            html += "&nbsp;&nbsp;<span class='comment-ajax-link delete-comment' data-parentid='" + id + "'><span class='text'>" + this.locale.deleteText + "</span></span>";
        }

        html += "</div>";

        html += "</div>";
        return html;
    };

    // Get comment items from SP list
    commentsList.prototype.getCommentItems = function (articleId, callback) {
        var query = "<Query>";
        query += "<Where>";
        query += "  <Eq>";
        query += "      <FieldRef " + (this.options.commentsArticleIdFieldType == "Lookup" ? "LookupId='TRUE' " : "") + "Name='" + this.options.commentsArticleIdFieldName + "' />";
        query += "      <Value Type='" + this.options.commentsArticleIdFieldType + "'>" + articleId + "</Value>";
        query += "  </Eq>";
        query += "</Where>";

        query += "<OrderBy>";
        query += "<FieldRef Name=\"Created_x0020_Date\" Ascending=\"TRUE\"/>";
        query += "</OrderBy>";

        query += "</Query>";

        var viewFields = "<ViewFields>";
        viewFields += "<FieldRef Name='ID' />";
        viewFields += "<FieldRef Name='" + this.options.commentsArticleIdFieldName + "' />";
        viewFields += "<FieldRef Name='" + this.options.commentsUserFieldName + "' />";
        viewFields += "<FieldRef Name='" + this.options.commentsParentFieldName + "' />";
        viewFields += "<FieldRef Name='" + this.options.commentsTextFieldName + "' />";
        viewFields += "</ViewFields>";


        //Query SharePoint list items
        SPData.getSPListItems(this.options.commentsListName, "", query, viewFields, 1000, "", function (listItems) {
            callback(listItems);
        }, function () {
            //TODO: catch exception
            callback(null);
        });
    };
    // Delete comment
    commentsList.prototype.deleteComment = function (commentId, root) {
        //query child comments:
        var query = "<Query>";
        query += "<Where>";
        query += "  <Eq>";
        query += "      <FieldRef Name='" + this.options.commentsParentFieldName + "' />";
        query += "      <Value Type='Int'>" + commentId + "</Value>";
        query += "  </Eq>";
        query += "</Where>";

        query += "</Query>";

        var viewFields = "<ViewFields>";
        viewFields += "<FieldRef Name='ID' />";
        viewFields += "</ViewFields>";

        var $this = this;
        //Query SharePoint list items
        SPData.getSPListItems(this.options.commentsListName, "", query, viewFields, 1000, "", function (listItems) {
            if (listItems.length > 0) {
                for (var i = 0; i < listItems.length; i++) {
                    $this.deleteComment(listItems[i].ID, false);
                }
            }
        }, function () {
            //TODO: catch exception

        });
        //delete root item
        SPData.deleteSPListItems(this.options.commentsListName, { ID: commentId }, function () {
            if (root)
                $this.loadCommentItems($this.commentItemsWrapper);
        });
    };
    //Get id of current article item
    commentsList.prototype.getCurrentArticleId = function () {
        if (typeof (this.options.currentId) != 'undefined')
            return this.options.currentId;
        return this.queryString(this.options.queryParameterName);
    };
    /*============== HELPERS ==================*/
    commentsList.prototype.queryString = function (ji) {
        var hu = window.location.search.substring(1);
        var gy = hu.split("&");
        for (var i = 0; i < gy.length; i++) {
            var ft = gy[i].split("=");
            if (ft[0].toLowerCase() == ji.toLowerCase()) {
                return ft[1];
            }
        }
        return null;
    };
    commentsList.prototype.S4 = function () {
        return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
    };
    commentsList.prototype.newGuid = function () {
        return (this.S4() + this.S4() + "-" + this.S4() + "-" + this.S4() + "-" + this.S4() + "-" + this.S4() + this.S4() + this.S4());
    };

    String.prototype.endsWith = function (suffix) {
        return this.indexOf(suffix, this.length - suffix.length) !== -1;
    };

    commentsList.prototype.defaults = function () {
        return {
            queryParameterName: "id",
            enableDelete: false,
            minCommentsToShowBottomForm: 10,

            commentsListName: "Comments",

            commentsArticleIdFieldName: "NewsId",
            commentsArticleIdFieldType: "Int", //Article field type

            commentsTextFieldName: "CommentText",
            commentsUserFieldName: "Author",
            commentsParentFieldName: "ParentId",

            articleCommentsCountFieldName: "",
            articleListName: ""
        };
    };

    return commentsList;
})();